Categories
Vue

Vue Select — Pagination, Infinite Scrolling, and Disabling Selections

Spread the love

To make dropdowns easier, we can use the Vue Select plugin to add the dropdown.

It can do much more than the select element.

In this article, we’ll look at how to use the vue-select package to make more complex dropdowns.

Selectable Prop

We can set which options are selected able with the selectable prop.

For example, we can write:

<template>
  <div id="app">
    <v-select
      placeholder="Choose fruit"
      :options="options"
      :selectable="option => option !== 'grape'"
    ></v-select>
  </div>
</template>
<script>
export default {
  name: "App",
  data() {
    return {
      options: ["apple", "orange", "grape"]
    };
  }
};
</script>

We set the selectable prop to a function that returns the condition for the items that we want to disable.

Therefore, we disable the 'grape' choice in the dropdown.

The placeholder has the placeholder for the dropdown.

Limiting the Number of Selections

We can limit the number of selections with the selectable prop.

For example, we can write:

<template>
  <div id="app">
    <v-select
      placeholder="Choose fruit"
      :options="options"
      multiple
      v-model="selected"
      :selectable="() => selected.length < 3"
    ></v-select>
  </div>
</template>
<script>
export default {
  name: "App",
  data() {
    return {
      selected: [],
      options: ["apple", "orange", "grape", "banana", "pear"]
    };
  }
};
</script>

The selectable prop is set to a function that checks the selected array’s length.

If we have more than 3, then the choices will be disabled.

Pagination

We can paginate the choices in the dropdown.

For example, we can write:

<template>
  <v-select :options="paginated" @search="query => search = query" :filterable="false">
    <li slot="list-footer" class="pagination">
      <button @click="offset -= 10" :disabled="!hasPrevPage">Prev</button>
      <button @click="offset += 10" :disabled="!hasNextPage">Next</button>
    </li>
  </v-select>
</template>

<script>
import countries from "./countries";
export default {
  data: () => ({
    countries,
    search: "",
    offset: 0,
    limit: 10
  }),
  computed: {
    filtered() {
      return this.countries.filter(country => country.includes(this.search));
    },
    paginated() {
      return this.filtered.slice(this.offset, this.limit + this.offset);
    },
    hasNextPage() {
      const nextOffset = this.offset + 10;
      return Boolean(
        this.filtered.slice(nextOffset, this.limit + nextOffset).length
      );
    },
    hasPrevPage() {
      const prevOffset = this.offset - 10;
      return Boolean(
        this.filtered.slice(prevOffset, this.limit + prevOffset).length
      );
    }
  }
};
</script>

The countries array is from the countryList in https://gist.github.com/incredimike/1469814.

We populate the list-footer slot with buttons to let us move through the pages.

Then we create the paginated computed property to get the items on a given page.

Also, we have the hasNextPage method to check if there’s the next page by seeing if there’s anything returned by slice .

And we use a similar logic with the hasPrevPage computed property.

The search event is emitted when we enter something in the search box.

Infinite Scrolling

We can also add infinite scrolling to our v-select dropdown.

For example, we can write:

<template>
  <v-select
    :options="paginated"
    :filterable="false"
    @open="onOpen"
    @close="onClose"
    @search="query => search = query"
  >
    <template #list-footer>
      <li ref="load" class="loader" v-show="hasNextPage">Loading more options...</li>
    </template>
  </v-select>
</template>

<script>
import countries from "./countries";

export default {
  data: () => ({
    observer: null,
    limit: 10,
    search: ""
  }),
  mounted() {
    this.observer = new IntersectionObserver(this.infiniteScroll);
  },
  computed: {
    filtered() {
      return countries.filter(country => country.includes(this.search));
    },
    paginated() {
      return this.filtered.slice(0, this.limit);
    },
    hasNextPage() {
      return this.paginated.length < this.filtered.length;
    }
  },
  methods: {
    async onOpen() {
      if (this.hasNextPage) {
        await this.$nextTick();
        this.observer.observe(this.$refs.load);
      }
    },
    onClose() {
      this.observer.disconnect();
    },
    async infiniteScroll([{ isIntersecting, target }]) {
      if (isIntersecting) {
        const ul = target.offsetParent;
        const scrollTop = target.offsetParent.scrollTop;
        this.limit += 10;
        await this.$nextTick();
        ul.scrollTop = scrollTop;
      }
    }
  }
};
</script>

We use the Intersection Observer API to check if we scrolled to the bottom of the list.

We start observing after the onOpen method is called when we open the dropdown.

Then we stop observing when the onClose method is called to stop observing.

The this.observer property is created from the IntersectionObserver constructor with the infiniteScroll method.

We can get whether we scrolled to the bottom of the dropdown list with the isIntersecting property.

Conclusion

We can add pagination and infinite scrolling with the dropdown with the Vue Select dropdown.

Also, we can disable and limit selections that we can choose.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *